Search in Rotated Sorted Array(33-Medium)
Description
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
Your algorithm’s runtime complexity must be in the order of O(log n).
Example
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Solution
利用二分查找:O(log n)
class Solution {
public int search(int[] nums, int target) {
if(nums.length == 0)
return -1;
int l =0;
int r = nums.length - 1;
while(l <= r){
int m = (l + r) / 2;
if(target == nums[l])
return l;
if(target == nums[m])
return m;
if(target == nums[r])
return r;
if(target > nums[l] && target < nums[m]){
r = m - 1;
continue;
}else if(target > nums[m] && target < nums[r]){
l = m + 1;
continue;
}else if(nums[l] > nums[m]){
r = m -1;
continue;
}else if(nums[m] > nums[r]){
l = m + 1;
continue;
}
}
return -1;
}
}
优化:
public int search(int[] nums, int target) {
int lo = 0, hi = nums.length - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid]))
lo = mid + 1;
else
hi = mid;
}
return lo == hi && nums[lo] == target ? lo : -1;
}
Find First and Last Position of Element in Sorted Array(34-Medium)
Description
Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
Example
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
Solution
改进基本的二分查找:
在二分查找中基本的条件:
1)if(nums[mid] > target) right = mid -1;
2) if(nums[mid] < target) left = mid + 1;
3) if(nums[mid] = target) right = mid,找到目标值
在本题中查找first和last目标值,将分为两部分查找,先找first,再找 last。
1.Search first
first的查找应该使查找范围尽量靠左,尽管数组中可能有多个target,但是应该找到最左面的
[1,3,5,5,5,5,9], target = 5
l = 1; r = 9; mid = 5(2);
mid == 5,这回应该尽量向左找,因为此时的5可能不是first,r = mid
l = 1; r = mid = 5; mid = 3;
mid < 5,这时时基本的二分查找条件,l = mid + 1
l = r = 5, 循环结束
2. Search last
和first相反,last应该使查找范围尽量靠右
同样的例子,[1,3,5,5,5,5,9], target = 5
l = 1; r = 9; mid = 5(2);
mid == 5,这回应该尽量向右找,因为此时的5可能不是last,l = mid
l = mid = 5, r = 9, mid = 5(3)
mid = 5, 尽量向右找, l = mid
l = mid = 5 , r = 9 , mid = 5(4)
mid = 5 , 尽量向右找, l = mid
l = mid = 5, r = 9, mid = 5,----?
这时出问题了,最后的范围只剩下[5,9]但是取中值的时候,mid总是等于5,跳不出循环,
我们想让mid更靠右,因为last target 更在乎它右边的值,而first更在乎它左边的值,
但是 mid = (l + r)/ 2 的公式更偏向与左边,索引在找last的时候讲中值公式更改为 mid = (l + r)/ 2 + 1
索引最后一次 mid = 9
mid > 5,也就是说右边从这个值开始不会再有5了,而最后一个5在m - 1,r = m - 1
通过first, last 的差异来进行两次循环
solution 1:
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] index = {-1,-1};
if(nums.length == 0)
return index;
int l = 0;
int r = nums.length - 1;
//第一次:找到第一个目标值索引
while(l < r) {
int m = (l + r) / 2;
if(nums[m] < target) {
l = m + 1;
}else {
r = m;
}
}
if(nums[l] != target) {
return index;
}else {
index[0] = l;
}
//第二次:找到最后一个目标值的索引
r = nums.length - 1;
while(l < r) {
int m = (l + r) / 2 + 1;
if(nums[m] > target) {
r = m - 1;
}else {
l = m;
}
}
index[1] = r;
return index;
}
}
solution 2
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] index = {-1,-1};
if(nums.length == 0) {
return index;
}
int left_index = Index(nums,target,true);
if(left_index == nums.length || nums[left_index] != target) {
return index;
}else {
index[0] = left_index;
}
index[1] = Index(nums, target,false) - 1;
return index;
}
public int Index(int[] nums, int target,boolean left) {
int l = 0;
int r = nums.length;
while(l < r) {
int m = (l + r) / 2;
if(nums[m] > target || (left && nums[m] == target)) {
r = m;
}else {
l = m + 1;
}
}
return l;
}
}
时间复杂度 O(log n)